use super::*;
use lexer::*;

// ## 5. Glyph substitution (GSUB) rules

// ### 5.a. [GSUB LookupType 1] Single substitution
// ### 5.b. [GSUB LookupType 2] Multiple substitution
// ### 5.c. [GSUB LookupType 3] Alternate substitution
// ### 5.d. [GSUB LookupType 4] Ligature substitution
// ### 5.e. [GSUB LookupType 5] Contextual substitution
// ### 5.f. [GSUB LookupType 6] Chaining contextual substitution
// #### 5.f.i. Specifying a Chain Sub rule and marking sub-runs
// #### 5.f.ii. Specifying exceptions to the Chain Sub rule
// ### 5.g. [GSUB LookupType 7] Extension substitution
// ### 5.h. [GSUB LookupType 8] Reverse Chaining Single Substitution
#[derive(Debug, Clone)]
pub struct Substitution {
	pub items: Vec<SubItem>,
	pub reverse: bool,
	pub ignore: bool,
}
impl Substitution {
	pub fn parse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut items = vec![];
		loop {
			match r.read_next()? {
				Event::Semi => break,
				Event::Comma => items.push(SubItem::Comma),
				//
				Event::ByKw => items.push(SubItem::By),
				Event::FromKw => items.push(SubItem::From),
				//
				Event::Ident(v) => items.push(SubItem::Class(Class(vec![ClassItem::Glyph(v.into())]))),
				Event::Group(v) => items.push(SubItem::Class(Class(vec![ClassItem::Ref(v.into())]))),
				Event::LSquare => items.push(SubItem::Class(Class::parse(r)?)),
				Event::NullKw => items.push(SubItem::Class(Class(vec![]))),
				//
				Event::SingleQuote => {
					let last = items.pop().ok_or(Error::Msg("quote must after glyphseq"))?;
					match last {
						SubItem::Class(seq) => items.push(SubItem::Quote(seq)),
						_ => return Err(Error::Msg("quote must after glyphseq")),
					}
				}
				Event::LookupKw => {
					let last = items.pop().ok_or(Error::Msg("lookup must after quote"))?;
					match last {
						SubItem::Quote(seq) => items.push(SubItem::LookUp(seq, vec![r.expect_ident()?.into()])),
						SubItem::LookUp(seq, mut lookups) => {
							lookups.push(r.expect_ident()?.into());
							items.push(SubItem::LookUp(seq, lookups));
						}
						_ => return Err(Error::Msg("lookup must after quote")),
					}
				}
				e => return Err(Error::Unexpect(format!("{:?}", e))),
			}
		}
		Ok(Self {
			items,
			reverse: false,
			ignore: false,
		})
	}
	pub fn parse_ignore(r: &mut Lexer<'_>) -> Result<Vec<Self>, Error> {
		let this = Self::parse(r)?;
		Ok(this
			.items
			.split(|v| matches!(v, SubItem::Comma))
			.map(|items| Self {
				items: items.to_owned(),
				ignore: true,
				reverse: false,
			})
			.collect())
	}
	pub fn parse_reverse(r: &mut Lexer<'_>) -> Result<Self, Error> {
		let mut this = Self::parse(r)?;
		this.reverse = true;
		Ok(this)
	}
}
#[derive(Debug, Clone)]
pub enum SubItem {
	Class(Class),
	Quote(Class),
	LookUp(Class, Vec<String>),
	//
	From,
	By,
	//
	Comma,
}

#[cfg(test)]
mod tests {
	use super::*;
	type S = Substitution;
	#[test]
	fn sub_single() {
		// substitute <glyph> by <glyph>;            # format A
		// substitute <glyphclass> by <glyph>;       # format B
		// substitute <glyphclass> by <glyphclass>;  # format C
		_ = S::parse(&mut Lexer::new("a by A.sc;")).unwrap();
		_ = S::parse(&mut Lexer::new("[one.fitted one.oldstyle one.tab.oldstyle] by one;")).unwrap();
		_ = S::parse(&mut Lexer::new("[a - z] by [A.sc - Z.sc];")).unwrap();
		_ = S::parse(&mut Lexer::new("@Capitals by @CapSwashes;")).unwrap();
		_ = S::parse(&mut Lexer::new("a by NULL;")).unwrap();
	}
	#[test]
	fn sub_multiple() {
		// substitute <glyph> by <glyph sequence>;
		_ = S::parse(&mut Lexer::new("f_f_i by f f i;")).unwrap();
	}
	#[test]
	fn sub_alternate() {
		// substitute <glyph> from <glyphclass>;
		_ = S::parse(&mut Lexer::new("ampersand from [ampersand.1 ampersand.2 ampersand.3];")).unwrap();
	}
	#[test]
	fn sub_ligature() {
		// substitute <glyph sequence> by <glyph>;
		let content = "[one one.oldstyle] [slash fraction] [two two.oldstyle] by onehalf;";
		_ = S::parse(&mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn sub_contextual() {
		_ = S::parse(&mut Lexer::new("[a e i o u] f' lookup CNTXT_LIGS i' n' lookup CNTXT_SUB;")).unwrap();
		_ = S::parse(&mut Lexer::new("[a e i o u] c' lookup CNTXT_LIGS t' s' lookup CNTXT_SUB;")).unwrap();
		_ = S::parse(&mut Lexer::new("[a e n] d' by d.alt;")).unwrap();
		_ = S::parse(&mut Lexer::new("[A-Z] [A.sc-Z.sc]' by [a-z];")).unwrap();
		_ = S::parse(&mut Lexer::new("[e e.begin]' t' c by ampersand;")).unwrap();
		_ = S::parse(&mut Lexer::new("ka' lookup REMOVE_CAKRA lookup REORDER_CAKRA ka.pas_cakra.ns';")).unwrap();
	}
	#[test]
	fn sub_ignore() {
		// ignore substitute <backtrack glyph sequence>*
		//     <marked glyph sequence>
		//     <lookahead glyph sequence>*;
		// ignore substitute <match sequence1>, <match sequence2>, <match sequence3>;
		let content = "feature liga {ignore sub f [a e] d', @LETTER f' i',@LETTER @BEGINNINGS';} liga;";
		_ = Fea::parse("", &mut Lexer::new(content)).unwrap();
		let content = "feature liga {ignore sub @LETTER a' n' d', a' n' d' @LETTER;} liga;";
		_ = Fea::parse("", &mut Lexer::new(content)).unwrap();
	}
	#[test]
	fn sub_reverse() {
		_ = Fea::parse("", &mut Lexer::new("feature liga {reversesub [a e n] d' by d.alt;} liga;")).unwrap();
	}
}
